perm filename NF.SAI[4,KMC] blob sn#180017 filedate 1975-10-04 generic text, type C, neo UTF8
COMMENT ⊗   VALID 00013 PAGES
C REC  PAGE   DESCRIPTION
C00001 00001
C00002 00002	BEGIN
C00004 00003	α INIT_TABLE INIT_TABLES
C00007 00004	α LEARN DUMP_DATA
C00010 00005	α LOOK_UP LOOK_UP_xxxxxx
C00013 00006	α OPEN_DISK INITIALIZE
C00015 00007	α SUBSET RESULT
C00017 00008	α FIND_WORD
C00022 00009	α VOCABULARY
C00023 00010	α FIND_WORDS ANAPH_REF
C00026 00011	α CANONIZE
C00029 00012	α MATCH1 MATCH
C00032 00013	α This selects the requested routine
C00033 ENDMK
C⊗;
BEGIN

REQUIRE "IODEFS.SAI[SEC,RCP]" SOURCE_FILE;

SAFE STRING ARRAY CLEAN, NEAR[0:127];

PROC INIT_CHAR(VALUE STRING SPECIAL, NEARBY);
	BEGIN
	STRING GROUP, VAL;
	INTEGER CHAR;
α Establish the character conversions;
	FOR I ←  0  TIL 127 DO CLEAN[I] ← " ";
	FOR I ← "0" TIL "9" DO CLEAN[I] ← I;
	FOR I ← "A" TIL "Z" DO CLEAN[I] ← I;
	FOR I ← "a" TIL "z" DO CLEAN[I] ← I - 32;
	WHILE (GROUP ← SCAN(SPECIAL, 2, BRK)) DO
		BEGIN
		VAL ← GROUP[1 TO 1];
		WHILE (CHAR ← LOP(GROUP)) DO CLEAN[CHAR] ← VAL;
		END;
α Establish character "nearness";
	FOR I ←  0  TIL 127 DO NEAR[I] ← NULL;
	WHILE (GROUP ← SCAN(NEARBY, 2, BRK)) DO
		BEGIN
		VAL ← LOP(GROUP);
		WHILE (CHAR ← LOP(GROUP)) DO NEAR[CHAR] ← NEAR[CHAR] & VAL;
		END;
	END;

STRING PROC CLEAN_UP;
	BEGIN
	INTEGER CHAR;
	STRING LINE, CLEANED;
	LINE ← IF FILE THEN IN_LINE ELSE GET_A_STRING("Type garbage:");
	CLEANED ← NULL;
	WHILE (CHAR ← LOP(LINE)) DO CLEANED ← CLEANED & CLEAN[CHAR];
	OUT_LINE(CLEANED);
	RETURN(CLEANED);
	END;

BOOLEAN PROC MEM(VALUE STRING WORD, S1, S2, S3);
	RETURN(EQU((WORD ← WORD[1 TO 3]), S1) OR EQU(WORD, S2) OR EQU(WORD, S3));
α INIT_TABLE INIT_TABLES;

SAFE INTEGER ARRAY BOUNDS[1:8];
DEFINE USE_PREFIX="1", USE_SUFFIX="3", USE_DICTIO="5", USE_MULTI="7";

INTEGER AFFIX_LEN;

PROC INIT_TABLE(VALUE STRING FNAME; REFERENCE STRING ARRAY TABLE, ON, MAKES;
		VALUE INTEGER USE_TABLE);
	BEGIN
	STRING LINE;
	INTEGER SIZE;
	FILIN(FNAME & "[PAR,RCP]", INCH2);
	EAT_DIR(INCH2);
	SIZE ← 0;
	WHILE (LINE ← INPUT(INCH2, 1)) AND (SIZE ← SIZE+1) ≤ BOUNDS[USE_TABLE+1] DO
		BEGIN
		TABLE[SIZE] ← SCAN(LINE, 3, BRK);
		IF MEM(FNAME,"PRE","SUF",NULL) THEN ON[SIZE] ← (SCAN(LINE, 3, BRK));
		MAKES[SIZE] ← SCAN(LINE, 4, BRK);
		END;
	IF SIZE > BOUNDS[USE_TABLE+1] THEN
		BEGIN
		OUTSTR(FNAME ∂ "table full" ↓);
		SIZE ← SIZE - 1;
		END;
	BOUNDS[USE_TABLE] ← SIZE;
	RELEASE(INCH2);
	END;

SAFE STRING ARRAY PREFIX, P_ON, P_MAKES[1:15];
SAFE STRING ARRAY SUFFIX, S_ON, S_MAKES[1:60];
SAFE STRING ARRAY DICTIO, MEANS[1:2000];
SAFE STRING ARRAY MULTI, SIMP[1:50];

PROC INIT_TABLES;
	BEGIN
	AFFIX_LEN ← 5;
α Read the table of prefixes;
	BOUNDS[USE_PREFIX+1] ← 15;
	INIT_TABLE("PREFIX", PREFIX, P_ON, P_MAKES, USE_PREFIX);
α Read the table of suffixes;
	BOUNDS[USE_SUFFIX+1] ← 60;
	INIT_TABLE("SUFFIX", SUFFIX, S_ON, S_MAKES, USE_SUFFIX);
α Read the dictionary;
	BOUNDS[USE_DICTIO+1] ← 2000;
	INIT_TABLE("DICTIO", DICTIO, MEANS, MEANS, USE_DICTIO);
α Read the multi-word entries;
	BOUNDS[USE_MULTI+1] ← 50;
	INIT_TABLE("MULTI", MULTI, SIMP, SIMP, USE_MULTI);
	END;
α LEARN DUMP_DATA;

STRING PROC LEARN(VALUE STRING WORD);
	BEGIN
	STRING MEANING;
	MEANING ← GET_A_STRING("What is" ∂ WORD ∂ "[<CR> for nothing]");
	IF MEANING THEN
		BEGIN
		INTEGER NEXT;
		NEXT ← BOUNDS[USE_DICTIO] +1;
		IF NEXT > BOUNDS[USE_DICTIO+1] THEN
			OUTSTR("DICTIO table full" ↓)
		ELSE	BEGIN
			BOUNDS[USE_DICTIO] ← NEXT;
			WHILE (NEXT ← NEXT-1) > 0 AND
				ALPHA(WORD, DICTIO[NEXT]) > 0 DO
				BEGIN
				DICTIO[NEXT+1] ← DICTIO[NEXT];
				MEANS[NEXT+1] ← MEANS[NEXT];
				END;
			DICTIO[NEXT + 1] ← WORD;
			MEANS[NEXT + 1] ← MEANING;
			END;
		RETURN(WORD);
		END
	ELSE RETURN(NULL);
	END;

PROC DUMP_DATA;
	BEGIN
	IF GET_A_STRING("NEAR [Y,N]") = "Y" THEN
	FOR I ← 0 TIL 127 DO
		IF NEAR[I] THEN OUTSTR(I ∂ "might be" ∂ NEAR[I] ↓);
	IF GET_A_STRING("PREFIX [Y,N]") = "Y" THEN
	FOR I ← 1 TIL BOUNDS[USE_PREFIX] DO
		OUTSTR(PREFIX[I] ∂ "-" ∂ P_ON[I] & TAB & "→" ∂ P_MAKES[I] ↓);
	IF GET_A_STRING("SUFFIX [Y,N]") = "Y" THEN
	FOR I ← 1 TIL BOUNDS[USE_SUFFIX] DO
		OUTSTR(S_ON[I] ∂ "-" ∂ SUFFIX[I] & TAB & "→" ∂ S_MAKES[I] ↓);
	IF GET_A_STRING("DICTIO [Y,N]") = "Y" THEN
	FOR I ← 1 TIL BOUNDS[USE_DICTIO] DO
		OUTSTR(DICTIO[I] & TAB & "→" ∂ MEANS[I] ↓);
	IF GET_A_STRING("DICTIO.NEW [Y,N]") = "Y" THEN
		BEGIN
		FILOUT("DICTIO.NEW[PAR,RCP]", OUCH2);
		FOR I ← 1 TIL BOUNDS[USE_DICTIO] DO
			OUT(OUCH2, DICTIO[I] & TAB & TAB & TAB & MEANS[I] ↓);
		RELEASE(OUCH2);
		END;
	IF GET_A_STRING("MULTI [Y,N]") = "Y" THEN
	FOR I ← 1 TIL BOUNDS[USE_MULTI] DO
		OUTSTR(MULTI[I] & TAB & "→" ∂ SIMP[I] ↓);
	END;
α LOOK_UP LOOK_UP_xxxxxx;

α Returns index of word in TABLE or else -(index of nearest preceding entry);

INTEGER WINDOWS;

INTEGER PROC LOOK_UP(VALUE STRING WORD; REFERENCE STRING ARRAY TABLE;
		VALUE INTEGER USE_TABLE);
	BEGIN
	INTEGER LB, UB;
	IF WINDOWS ≥ 10 THEN OUTSTR("Looking for:" ∂ WORD ↓);
	LB ← 0;
	UB ← BOUNDS[USE_TABLE] + 1;
	WHILE UB > LB+1 DO
		BEGIN
		INTEGER AT, COMP;
		AT ← (LB + UB) DIV 2;
		IF (COMP ← ALPHA(WORD, TABLE[AT])) = 0 THEN
			BEGIN
			WHILE AT > 1 AND EQU(WORD, TABLE[AT-1]) DO AT ← AT-1;
			RETURN(AT);
			END;
		IF COMP > 0 THEN UB ← AT ELSE LB ← AT;
		END;
	RETURN(-LB);
	END;

α Returns index of word in PREFIX or else -(index of nearest preceding entry);

INTEGER PROC LOOK_UP_PREFIX(VALUE STRING WORD);
	RETURN(LOOK_UP(WORD, PREFIX, USE_PREFIX));

α Returns index of word in SUFFIX or else -(index of nearest preceding entry);

INTEGER PROC LOOK_UP_SUFFIX(VALUE STRING WORD);
	RETURN(LOOK_UP(WORD, SUFFIX, USE_SUFFIX));

α Returns index of word in DICTIO or else -(index of nearest preceding entry);

INTEGER PROC LOOK_UP_DICTIO(VALUE STRING WORD);
	RETURN(LOOK_UP(WORD, DICTIO, USE_DICTIO));

α Returns index of word in MULTI or else -(index of nearest preceding entry);

INTEGER PROC LOOK_UP_MULTI(VALUE STRING WORD);
	RETURN(LOOK_UP(WORD, MULTI, USE_MULTI));
α OPEN_DISK INITIALIZE;

BOOLEAN ROGER;

PROC OPEN_DISK;
	BEGIN
	RELEASE(INCH1);
	RELEASE(OUCH1);
	FILE ← GET_A_STRING("Disk input file [<CR> for none]");
	IF FILE THEN
		BEGIN
		FILIN(FILE, INCH1);
		FILOUT(SCAN(FILE, 7, BRK) & ".CLN", OUCH1);
		FILE ← "Y";
		END
	ELSE FILOUT("TYPED.CLN", OUCH1);
	END;

BOOLEAN LEARNING, RESPELLED;
INTEGER MISSPELLED, GIBBERISH;

PROC INITIALIZE;
	BEGIN
	ROGER ← TRUE;
	BREAK_LINE;
	BREAK_BLANK;
	BREAK_TAB;
	BREAK_EXT;
	SETBREAK(10, " ,.?", NULL, "IRN");
	OPEN_DISK;
	INIT_CHAR("' ,;: .!) ?",
		"WQ TY YTUI UY EI IE '7 O0P AS SA DS GH HG NM MN");
	INIT_TABLES;
	IF ROGER THEN WINDOWS ← 6 ELSE
	WINDOWS ← GET_AN_INT("Windows = [0-10]");
	IF ROGER THEN LEARNING ← TRUE ELSE
	LEARNING ← GET_A_STRING("Learning [Y,N]") = "Y";
	RESPELLED ← FALSE;
	MISSPELLED ← GIBBERISH ← 0;
	END;
α SUBSET RESULT;

BOOLEAN PROC SUBSET(VALUE STRING ROOT, SSET);
	BEGIN
	STRING WORD;
	IF WINDOWS ≥ 8 THEN OUTSTR(ROOT ∂ "⊂" ∂ SSET ↓);
	IF EQU(SSET, "word") THEN RETURN(TRUE);
	WHILE (WORD ← SCAN(ROOT, 2, BRK)) DO
		BEGIN
		INTEGER FOUND;
		IF LOP(ROOT) = "←" THEN WORD ← SCAN(ROOT, 2, BRK);
		IF WORD = "@" THEN WORD ← WORD[2 TO ∞];
		IF EQU(WORD, SSET) THEN RETURN(TRUE);
		IF EQU(WORD, "word") THEN RETURN (FALSE);
		IF (FOUND ← LOOK_UP_DICTIO(WORD)) > 0 THEN ROOT ← MEANS[FOUND]
		ELSE DONE;
		IF WINDOWS ≥ 8 THEN OUTSTR(ROOT ∂ "⊂" ∂ SSET ↓);
		END;
	OUTSTR("Can't find" ∂ WORD ∂ "in DICTIO" ↓);
	RETURN(FALSE);
	END;

α Attaches information gained from affixes to root;

STRING PROC RESULT(VALUE STRING WORD, MAKES);
	BEGIN
	IF ¬MAKES THEN RETURN(WORD);
	IF MAKES = "←" THEN
		BEGIN
		STRING ROOT, TEMP;
		ROOT ← SCAN(WORD, 2, BRK);
		IF WORD = "←" THEN TEMP ← SCAN(WORD, 2, BRK);
		IF WORD THEN MAKES ← MAKES ∂ WORD;
		RETURN(ROOT ∂ MAKES);
		END;
	RETURN(WORD ∂ MAKES);
	END;
α FIND_WORD;

REQUIRE 100 STRING_PDL;

α Should accept predicted value;
α Should catch numbers (before splitting into 2 words);

RECURSIVE STRING PROC FIND_WORD(VALUE STRING WORD);
	BEGIN
	INTEGER FOUND, SPLIT, LN_WORD;
	STRING ROOT;
	LN_WORD ← LN(WORD);
α Try to find the word in the dictionary;
	IF LOOK_UP_DICTIO(WORD) > 0 THEN RETURN(WORD);
α Try adding an "E" at the end (short cut for the suffixer);
	IF LOOK_UP_DICTIO(WORD & "E") > 0 THEN RETURN(WORD & "E");
	FOR SPLIT ← 2 MAX (LN_WORD-AFFIX_LEN+1) TIL LN_WORD DO
α Try to split the word into a root and a suffix;
		IF (FOUND ← LOOK_UP_SUFFIX(WORD[SPLIT TO ∞])) > 0 AND
			((ROOT ← FIND_WORD(WORD[1 TO SPLIT-1])) OR
			(ROOT ← FIND_WORD(WORD[1 TO SPLIT-1] & "E"))) THEN
			DO
			IF SUBSET(ROOT, S_ON[FOUND]) THEN
				BEGIN
				RESPELLED ← FALSE;
				RETURN(RESULT(ROOT, S_MAKES[FOUND]));
				END
			UNTIL ¬EQU(SUFFIX[FOUND], SUFFIX[(FOUND ← FOUND+1)]);
	FOR SPLIT ← LN_WORD MIN (AFFIX_LEN+1) STEP -1 UNTIL 2 DO
α Try to split the word into a prefix and a root;
		IF (FOUND ← LOOK_UP_PREFIX(WORD[1 TO SPLIT-1])) > 0 AND
			(ROOT ← FIND_WORD(WORD[SPLIT TO ∞])) AND
			SUBSET(ROOT, P_ON[FOUND]) THEN
			BEGIN
			RESPELLED ← FALSE;
			RETURN(RESULT(ROOT, P_MAKES[FOUND]));
			END;
	FOR SPLIT ← LN_WORD STEP -1 UNTIL 1 DO
		BEGIN
		STRING NEARBY, CHAR;
α Try replacing a letter with a nearby (or similar) letter;
		NEARBY ← NEAR[WORD[SPLIT FOR 1]];
		WHILE (CHAR ← LOP(NEARBY)) DO
		IF LOOK_UP_DICTIO(WORD[1 TO SPLIT-1] & CHAR &
				WORD[SPLIT+1 TO ∞]) > 0 THEN
			BEGIN
			RESPELLED ← TRUE;
			RETURN(WORD[1 TO SPLIT-1] & CHAR & WORD[SPLIT+1 TO ∞]);
			END;
α Try removing a letter;
		IF LOOK_UP_DICTIO(WORD[1 TO SPLIT-1] & WORD[SPLIT+1 TO ∞]) > 0 THEN
			BEGIN
			RESPELLED ← TRUE;
			RETURN(WORD[1 TO SPLIT-1] & WORD[SPLIT+1 TO ∞]);
			END;
		END;
α Try transposing 2 letters;
	FOR SPLIT ← 2 TIL LN_WORD DO
		IF LOOK_UP_DICTIO(WORD[1 TO SPLIT-2] & WORD[SPLIT FOR 1] &
				WORD[SPLIT-1 FOR 1] & WORD[SPLIT+1 TO ∞]) > 0 THEN
			BEGIN
			RESPELLED ← TRUE;
			RETURN(WORD[1 TO SPLIT-2] & WORD[SPLIT FOR 1] &
			WORD[SPLIT-1 FOR 1] & WORD[SPLIT+1 TO ∞]);
			END;
α Try to split the word into 2 words;
	FOR SPLIT ← 4 TIL LN_WORD-2 DO
		BEGIN
		IF LOOK_UP_DICTIO(WORD[1 TO SPLIT-1]) > 0 AND
			(ROOT ← FIND_WORD(WORD[SPLIT TO ∞])) THEN
			BEGIN
			RESPELLED ← TRUE;
			RETURN(WORD[1 TO SPLIT-1] ∂ ROOT);
			END;
		IF LOOK_UP_DICTIO(WORD[SPLIT TO ∞]) > 0 AND
			(ROOT ← FIND_WORD(WORD[1 TO SPLIT-1])) THEN
			BEGIN
			RESPELLED ← TRUE;
			RETURN(ROOT ∂ WORD[SPLIT TO ∞]);
			END;
		END;
α Ask for definition if in LEARNING mode;
	IF LEARNING THEN RETURN(LEARN(WORD));
α Nothing worked, give up;
	RETURN(NULL);
	END;
α VOCABULARY;

PROC VOCABULARY;
	BEGIN
	STRING LINE;
	LINE ← CLEAN_UP;
	WHILE ¬EOF DO
		BEGIN
		WHILE LINE DO
			BEGIN
			STRING WORD, ROOT;
			WORD ← SCAN(LINE, 10, BRK);
			IF ¬WORD THEN
				BEGIN
				WORD ← LOP(LINE);
				CONTINUE;
				END;
			ROOT ← FIND_WORD(WORD);
			IF ¬EQU(ROOT, WORD) AND
				(WORD ← GET_A_STRING(WORD ∂ "=" ∂ ROOT)) THEN
				BEGIN
				IF WORD = "?" THEN DUMP_DATA
				ELSE WORD ← LEARN(WORD);
				END;
			END;
		LINE ← CLEAN_UP;
		END;
	END;
α FIND_WORDS ANAPH_REF;

α Should return something besides "←name" for gibberish;

STRING PROC FIND_WORDS(VALUE STRING SENT);
	BEGIN
	STRING FOUND;
	FOUND ← NULL;
	WHILE SENT DO
		BEGIN
		STRING WORD, MEANING;
		WORD ← SCAN(SENT, 10, BRK);
		IF ¬WORD THEN
			BEGIN
			WORD ← LOP(SENT);
			IF WORD = " " THEN CONTINUE;
			END;
		IF (MEANING ← FIND_WORD(WORD)) THEN
			BEGIN
			IF RESPELLED THEN
				BEGIN
				MISSPELLED ← MISSPELLED + 1;
				RESPELLED ← FALSE;
				END;
			END
		ELSE	BEGIN
			MEANING ← WORD ∂ "←name";
			GIBBERISH ← GIBBERISH + 1;
			END;
		FOUND ← FOUND ∂ MEANING;
		END;
	IF WINDOWS ≥ 6 THEN
		BEGIN
		OUTSTR("Found:" ∂ FOUND[2 TO ∞] ↓);
		OUTSTR("Missp. =" ∂ CVS(MISSPELLED) & TAB &
			"Gibber. =" ∂ CVS(GIBBERISH) ↓);
		END;
	RETURN(FOUND[2 TO ∞]);
	END;

α Should handle multiple-word responses (like "YOUR MOTHER");

STRING PROC ANAPH_REF(VALUE STRING WORD, MEANING);
	BEGIN
	STRING REF;
	IF ROGER AND EQU(MEANING, "noun") THEN
		BEGIN
		IF EQU(WORD, "I") THEN RETURN("ROGER");
		IF EQU(WORD, "YOU") THEN RETURN("PARRY");
		IF EQU(WORD, "HERE") THEN RETURN("AT STANFORD");
		IF EQU(WORD, "THERE") THEN RETURN("IN THE HOSPITAL");
		END;
	DO REF ← FIND_WORDS(GET_A_STRING("Referent for:" ∂ WORD))
	UNTIL SUBSET(REF, MEANING);
	RETURN(REF);
	END;
α CANONIZE;

STRING MARKERS;

α Should not be dropping articles and adjectives;
α Should separate markers;
α Should type-check slot fillers;

STRING PROC CANONIZE(VALUE STRING SENT, SLOTS);
	BEGIN
	STRING WORD, MEANING, ANSWER;
	ANSWER ← NULL;
	WHILE (WORD ← SCAN(SENT, 2, BRK)) DO
		BEGIN
		IF WORD = "⊂" THEN
			BEGIN
			ANSWER ← ANSWER ∂ SCAN(SLOTS, 3, BRK);
			CONTINUE;
			END;
		IF WORD = "λ" THEN
			BEGIN
			ANSWER ← ANSWER ∂ WORD;
			CONTINUE;
			END;
		IF SENT = "←" THEN
			BEGIN
			MEANING ← SCAN(SENT, 2, BRK);
			WORD ← WORD ∂ MEANING;
			MEANING ← MEANING[2 TO ∞];
			END
		ELSE MEANING ← MEANS[LOOK_UP_DICTIO(WORD)];
		IF MEM(MEANING, "aux", "int", "nam") OR
			MEM(MEANING, "nou", "pre", "ver") THEN
			ANSWER ← ANSWER ∂ WORD
		ELSE IF MEM(MEANING, "adv", "mod", "mod") OR
			MEM(MEANING, "not", "ten", "wha") THEN
			MARKERS ← MARKERS ∂ MEANING & "=" & WORD
		ELSE IF MEM(MEANING, "adj", "art", NULL) THEN
			OUTSTR("Dropping" ∂ MEANING ∂ WORD ↓)
		ELSE IF MEANING = "@" THEN
			SENT ← ANAPH_REF(WORD, MEANING[2 TO ∞]) ∂ SENT
		ELSE IF MEANING = "con" THEN
			ANSWER ← ANSWER ∂ MEANING
		ELSE SENT ← MEANING ∂ SENT;
		END;
	IF WINDOWS ≥ 4 THEN OUTSTR("Canonized:" ∂ ANSWER[2 TO ∞] ↓);
	RETURN(ANSWER[2 TO ∞]);
	END;
α MATCH1 MATCH;

α Should be explicit verbs in patterns.;
α Should type-check slot fillers;
α Should not just drop clauses - save them for future processing;
α Should search backward so that SSORT ordering in MULTI would give
	longest-first.  (somehow);

STRING PROC MATCH1(VALUE STRING SENT);
	BEGIN
	STRING WORD, SENT1;
	SENT1 ← SENT;
	WORD ← SCAN(SENT1, 2, BRK);
	IF LOP(SENT1) = "←" THEN WORD ← SCAN(SENT1, 2, BRK);
	WHILE ¬EQU(WORD, "word") DO
		BEGIN
		INTEGER FOUND;
		FOUND ← ABS(LOOK_UP_MULTI(WORD)) + 1;
		WHILE EQU(WORD, MULTI[FOUND][1 TO LN(WORD)]) DO
			BEGIN
			STRING FOUND1, SLOTS;
			SENT1 ← SENT;
			FOUND1 ← MULTI[FOUND];
			SLOTS ← NULL;
			WHILE TRUE DO
				BEGIN
				STRING S, F;
				IF ¬(F ← SCAN(FOUND1, 2, BRK)) THEN
				BEGIN
				F ← CANONIZE(SIMP[FOUND], SLOTS[2 TO ∞]);
				IF F = "λ" THEN OUTSTR("Dropping clause:" ∂
					SCAN(F, 2, BRK) ∂ "with" ∂ SLOTS[2 TO ∞] ↓);
				IF F THEN SENT1 ← F ∂ SENT1;
				IF WINDOWS ≥ 2 THEN OUTSTR("Matched:" ∂ SENT1 ↓);
				RETURN(SENT1);
				END;
				IF ¬(S ← SCAN(SENT1, 2, BRK)) THEN DONE;
				IF SENT1 = "←" THEN S ← S ∂ SCAN(SENT1, 2, BRK);
				IF EQU(F, S) THEN CONTINUE;
				IF F ≤ "Z" OR ¬SUBSET(S, F) THEN DONE;
				SLOTS ← SLOTS & TAB & S;
				END;
			FOUND ← FOUND + 1;
			END;
		WORD ← MEANS[LOOK_UP_DICTIO(WORD)];
		END;
	OUTSTR("Can't find" ∂ SENT ∂ "in MULTI" ↓ &
		"	so dropping" ∂ SCAN(SENT, 2, BRK) ↓);
	RETURN(SENT);
	END;

α Should separate markers;

STRING PROC MATCH(VALUE STRING SENT);
	BEGIN
	MARKERS ← NULL;
	SENT ← CANONIZE(SENT, NULL);
	WHILE SENT DO SENT ← MATCH1(SENT);
	IF WINDOWS ≥ 2 THEN OUTSTR("Markers:" ∂ MARKERS ↓);
	RETURN(SENT);
	END;
α This selects the requested routine;

WHILE TRUE DO
	BEGIN
	STRING JOB;
	JOB ← GET_A_STRING("Initialize, set Windows, Dump data, Vocabulary,
Find words, Canonize, Match, eXit");
	     IF JOB = "I" THEN INITIALIZE
	ELSE IF JOB = "W" THEN WINDOWS ← GET_AN_INT("Windows = [0-10]")
	ELSE IF JOB = "D" THEN DUMP_DATA
	ELSE IF JOB = "V" THEN VOCABULARY
	ELSE IF JOB = "F" THEN JOB ← FIND_WORDS(CLEAN_UP)
	ELSE IF JOB = "C" THEN JOB ← CANONIZE(FIND_WORDS(CLEAN_UP), NULL)
	ELSE IF JOB = "M" THEN JOB ← MATCH(FIND_WORDS(CLEAN_UP))
	ELSE IF JOB = "X" THEN DONE
	ELSE OUTSTR("You blew it" ↓);
	END;

END